<template>
  <div class="department-container">
    <div class="app-container">
      <el-card v-loading="loading">
        <!-- 头部布局 -->
        <tree-tools :data="company" :show-btn="false" />
        <!-- 树形结构内容 -->
        <!-- 使用 el-tree 来渲染一个树形结构 -->
        <!-- data 用于指定要渲染的树形结构数据 -->
        <!-- 可以使用 props 来修改 el-tree 默认读取的字段 -->
        <el-tree :data="list" :props="defaultProps" default-expand-all>
          <!-- data：每个节点的数据 -->
          <template #default="{ data }">
            <tree-tools
              :data="data"
              :show-btn="true"
              @show-dialog="showDialog"
              @get-department-list="getDepartmentList"
            />
          </template>
        </el-tree>
      </el-card>

      <!-- .native 用法 -->
      <native-usage @click.native="handleClick" />

      <el-button @click="handleClick">按钮</el-button>

      <MyElForm ref="myElFormRef" />
      <el-button @click="handleClick1">清空表单</el-button>
      <el-button @click="handleClick2">兜底校验</el-button>
      <!-- <usage>
        <template #default="obj">
          <p>传递进去的插槽内容1 --- {{ obj.data }}</p>
          <p>传递进去的插槽内容1 --- {{ obj.age }}</p>
        </template>
      </usage>
      <usage>
        <template v-slot="{ data, age }">
          <h3>传递进去的插槽内容2 --- {{ data }}</h3>
          <h3>传递进去的插槽内容2 --- {{ age }}</h3>
        </template>
      </usage> -->
    </div>

    <!-- visible：用于控制弹窗的显示与隐藏 -->
    <el-dialog
      :title="departForm.id ? '编辑部门' : '添加子部门'"
      :visible="dialogVisible"
      width="40%"
      @close="closeDialog"
      @open="openDialog"
    >
      <!-- 表单 -->
      <el-form ref="departRef" :model="departForm" :rules="departRules" label-width="100px">
        <el-form-item label="部门名称" prop="name">
          <el-input v-model="departForm.name" placeholder="请输入部门名称" />
        </el-form-item>
        <el-form-item label="部门编码" prop="code">
          <el-input v-model="departForm.code" placeholder="请输入部门编码" />
        </el-form-item>
        <el-form-item label="部门负责人" prop="manager">
          <el-select v-model="departForm.manager" placeholder="请选择" style="width: 100%;">
            <el-option
              v-for="item in managerList"
              :key="item.id"
              :label="item.username"
              :value="item.username"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="部门介绍" prop="introduce">
          <el-input v-model="departForm.introduce" placeholder="请输入部门介绍" />
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="submit">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
// import Usage from '@/components/slotUsage.vue'
import TreeTools from './components/tree-tools.vue'
import { getDepartmentList, getManagerList, addDepartment, getDepartmentDetail, updateDepartment } from '@/api/department'

export default {
  components: {
    // Usage
    TreeTools
  },
  data() {
    // 声明部门名称的自定义校验规则函数
    const validateName = (rules, value, cb) => {
      // rules：指定了一些规则信息，基本用不到
      // value：表单项的内容（值）
      // cb：函数。这个函数有两种调用方式
      //    1.第一种调用方式： cb() 表示校验通过
      //    2.第二种调用方式： cb(new Error('提示给用户的错误信息')) 表示校验不通过

      // 同级部门名称不能一样
      let broArr = []
      if (this.departForm.id) {
        // 当前是编辑
        // 如果 name 和原来的 name 一样，直接校验通过
        if (value === this.currentData.name) {
          cb()
          return
        }
        // 在扁平化数据中，过滤出 正在操作的部门的所有的兄弟
        broArr = this.originList.filter(item => item.pid === this.currentData.pid)
      } else {
        // 当前是添加
        // 1. 先拿到所有的同级部门（就是正在操作的部门的所有的孩子），去做比较
        // 2. 在扁平化数据中，过滤出 正在操作的部门的所有的孩子
        broArr = this.originList.filter(item => item.pid === this.currentData.id)
      }
      // 3. 在所有的同级部门中，使用 some 寻找，是否有跟当前正在添加的部门名称一样的部门，
      //    如果有，表示校验不通过，如果没有，表示校验通过。
      const isSame = broArr.some(item => item.name === value)
      if (isSame) {
        // 校验不通过
        cb(new Error('同级部门名称不能一致！'))
      } else {
        // 校验通过
        cb()
      }
      // console.log(sonArr);
    }
    // 声明部门编码的自定义校验规则函数
    const validateCode = (rules, value, cb) => {
      // 编辑的情况下，code 跟原来的 code 一样，要校验通过
      if (this.departForm.id && value === this.currentData.code) {
        cb()
        return
      }
      // 所有部门编码都不能一样
      const isSame = this.originList.some(item => item.code === value)
      if (isSame) {
        // 校验不通过
        cb(new Error('部门编码不能相同！'))
      } else {
        // 校验通过
        cb()
      }
    }
    return {
      // label 指定的是树形结构中某个节点的文字内容
      // children 用于指定子节点内容
      list: [],
      defaultProps: {
        label: 'name'
        // children: 'child'
      },
      // 公司信息数据
      company: {
        name: '传智教育',
        manager: '负责人',
        id: ''
      },
      // 控制弹窗显示与隐藏的值
      dialogVisible: false,
      // 表单数据对象
      departForm: {
        name: '',
        code: '',
        manager: '',
        introduce: ''
      },
      // 表单验证规则对象
      departRules: {
        name: [
          { required: true, message: '部门名称不能为空', trigger: ['blur', 'change'] },
          { min: 1, max: 50, message: '部门名称要求1-50个字符', trigger: ['blur', 'change'] },
          // 自定义校验规则
          // { validator: 自定义校验规则函数, trigger: ['blur', 'change'] }
          { validator: validateName, trigger: ['blur', 'change'] }
        ],
        code: [
          { required: true, message: '部门编码不能为空', trigger: ['blur', 'change'] },
          { min: 1, max: 50, message: '部门编码要求1-50个字符', trigger: ['blur', 'change'] },
          { validator: validateCode, trigger: ['blur', 'change'] }
        ],
        manager: [
          { required: true, message: '部门负责人不能为空', trigger: ['blur', 'change'] }
        ],
        introduce: [
          { required: true, message: '部门介绍不能为空', trigger: ['blur', 'change'] },
          { min: 1, max: 300, message: '部门介绍要求1-300个字符', trigger: ['blur', 'change'] }
        ]
      },
      // 当前正在操作的那条数据
      currentData: {},
      // 后端返回的扁平化数组数据
      originList: [],
      // 部门负责人列表数据
      managerList: [],
      // 控制 loading 的显示与隐藏的值
      loading: false
    }
  },
  created() {
    this.getDepartmentList()
  },
  methods: {
    handleClick1() {
      this.$refs.myElFormRef.resetFields()
    },
    handleClick2() {
      this.$refs.myElFormRef.validate()
    },
    handleClick() {
      console.log('按钮被点击了')
    },
    // 将扁平化数据转换成树形结构数据
    tranTree(list) {
      // 入参 list：就是要转换的扁平化数据

      // 定义转换后的树形数据
      const treeList = []

      // 具体的转换工作

      // 转换思路：
      // 转换过程其实就是孩子寻找父亲的过程：
      //   1. 遍历扁平化数组
      //   2. 用数组中的某一项的 pid，去匹配另外一项的 id，如果相等，表示
      //      匹配到的就是父亲，就把自己加入到父亲的 children 里面。

      // 双重循环时间复杂度比较高，代码逻辑比较复杂难以理解
      // list.forEach(item => {
      //   item.pid
      //   list.forEach(item2 => {
      //     if (item.pid === item2.id) {}
      //   })
      // })

      // 为了更加方便的去寻找 id，我们要创建一个 id 和 item 的映射关系。
      // 扁平化数据 = [
      //   { id: "1175311373083017216", pid: "", name: "技术部" },
      //   { id: "1235398608847052800", pid: "1175311373083017216", name: "Java研发部" },
      //   { id: "1235398661355544576", pid: "1175311373083017216", name: "Python研发部" },
      //   { id: "1235398708763762688", pid: "1175311373083017216", name: "Php研发部" }
      // ]

      // obj[id]
      // id和item的映射关系obj = {
      //   "id": item
      //   "1175311373083017216": { id: "1175311373083017216", pid: "", name: "技术部", children: [
      //     {name: 'Java'},
      //     {name: 'Python'},
      //     {name: 'Php'}
      // ] },
      //   "1235398608847052800": { id: "1235398608847052800", pid: "1175311373083017216", name: "Java研发部" },
      //   "1235398661355544576": { id: "1235398661355544576", pid: "1175311373083017216", name: "Python研发部" },
      //   "1235398708763762688": { id: "1235398708763762688", pid: "1175311373083017216", name: "Php研发部" }
      // }
      const obj = {}
      list.forEach(item => {
        obj[item.id] = item
      })
      // console.log(obj);

      // 真正的数据转换工作
      list.forEach(item => {
        if (obj[item.pid]) {
          // 找到了父亲
          // item 是自己
          // obj[item.pid] 是父亲
          // 如果父亲还没有 children，我们需要手动为父亲添加一个 children 属性，值为空数组
          if (!obj[item.pid].children) obj[item.pid].children = []
          // 将自己添加到父亲的 children 里面
          obj[item.pid].children.push(item)
          // 注意：
          // 映射关系中的 item 和扁平化数据中的 item 是同一个引用。
          // 给映射关系中的 item 添加 children 属性，就相当于给扁平化数据的 item 添加 children 属性
        } else {
          // 没找到父亲
          treeList.push(item)
        }
      })
      // 返回值：就是转换后的树形结构数据
      return treeList
    },
    // 递归：函数调用自己
    // recursion() {
    //   // 1. 递归终止条件
    //   // 2. 处理当前层逻辑（要找到重复做的事情）
    //   // 3. 下钻到下一层
    // }
    // 使用递归实现树形数据的转换
    recurTransTree(list, id) {
      // 入参 list：就是要转换的扁平化数据
      // 入参 id：每次要找孩子的那个父节点 id

      // child：用于收集本次递归找到的所有的孩子
      const child = []

      // 转换工作
      // 遍历扁平化数组数据，开始收集孩子
      list.forEach(item => {
        // 递归终止条件
        if (item.pid === id) {
          // 处理当前层逻辑（要找到重复做的事情）
          // 如果某条数据的 pid 等于传递进来的父节点 id，说明该条数据是父节点的孩子，把它放入到收集孩子的数组中
          child.push(item)
          // 下钻到下一层
          // 继续递归，以该数据为父节点，去收集它的孩子
          const children = this.recurTransTree(list, item.id)
          // 如果收集到了 item 的孩子，再给 item 添加 children 属性
          if (children.length) item.children = children
        }
      })

      // 返回值 child：本次递归找到的所有的孩子
      return child
    },
    // 显示弹窗
    async showDialog(data, type) {
      this.dialogVisible = true
      // console.log(data);
      this.currentData = data
      // 只有编辑时，才做数据回显
      if (type === 'edit') {
        // 调用获取部门详细信息接口
        const res = await getDepartmentDetail(this.currentData.id)
        // 给表单数据对象赋值
        this.departForm = res.data
      }
    },
    // 当点击X和蒙板时，会触发 close 事件
    // 弹窗关闭时，也会触发 close 事件
    closeDialog() {
      this.dialogVisible = false
      // 重置表单：会清空表单内容，并移除校验结果
      this.$refs.departRef.resetFields()
      // 手动清空表单数据
      this.departForm = {
        name: '',
        code: '',
        manager: '',
        introduce: ''
      }
    },
    // 弹窗打开时的回调函数
    async openDialog() {
      // 调用获取部门负责人列表的接口
      const res = await getManagerList()
      // console.log(res);
      this.managerList = res.data
    },
    // 封装获取组织架构部门列表数据的方法
    async getDepartmentList() {
      // 发起请求之前开启 loading
      this.loading = true
      try {
        // 发起请求，获取组织架构部门列表数据
        const res = await getDepartmentList()
        // 接口请求成功之后，关闭 loading
        this.loading = false
        // console.log(res);
        // 后端返回的数扁平化数据
        // 我们要将扁平化数据转换成树形结构数据
        // this.list = this.tranTree(res.data.depts)
        this.list = this.recurTransTree(res.data.depts, '')
        this.originList = res.data.depts
      } catch (error) {
        // 接口调用失败，关闭 loading
        this.loading = false
      }
    },
    // 添加子部门
    submit() {
      // 兜底校验
      this.$refs.departRef.validate(async(valid) => {
        if (valid) {
          // 校验通过
          // 调用接口，发起请求
          // 区分调用添加还是编辑的接口
          if (this.departForm.id) {
            // 当前是编辑操作
            await updateDepartment(this.departForm)
          } else {
            // 当前是添加操作
            await addDepartment({
              ...this.departForm,
              pid: this.currentData.id
            })
          }
          // 提示用户
          const msg = this.departForm.id ? '编辑部门成功' : '添加子部门成功'
          this.$message.success(msg)
          // 关闭弹窗
          this.dialogVisible = false
          // 刷新列表
          this.getDepartmentList()
        }
      })
    }
  }
}
</script>

<style>

</style>
